#include "pch.hpp"
#include "Function.hpp"
#include "Type.hpp"
#include "Reflectable.hpp"

using namespace Engine;
using namespace Engine::Script;




Function::Function(boost::weak_ptr<const Reflectable> reflectable, pointer<asIScriptFunction> handle)
	: m_reflectable(reflectable)
	, m_handle(handle)
{
	verify(m_handle != nullptr)
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Function::~Function()
{}
///////////////////////////////////////////////////////////////////////////////////////////////////




int Function::id() const
{
	return m_handle->GetId();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

QString Function::name() const
{
	return QString::fromUtf8(m_handle->GetName());
}
///////////////////////////////////////////////////////////////////////////////////////////////////

QString Function::declaration() const
{
	QString v;
	QTextStream buffer(&v);

	Type::opt ret = returnType();
	buffer << (ret ? ret->name() : "void") << " " << name() << "(";

	for(int i = 0; i < argumentCount(); ++i)
	{
		asDWORD flags;
		int id = m_handle->GetParamTypeId(i, &flags);
		auto arg = reflectable()->type(id);

		if(i != 0)
			buffer << ", ";

		buffer << arg.name();

		if(flags & asTM_INREF)
			buffer << " &in";
		else if(flags & asTM_OUTREF)
			buffer << " &out";
		else if(flags & asTM_INOUTREF)
			buffer << " &inout";
	}

	buffer << ")";

	return v;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool Function::isFreeFunction() const
{
	return !isClassMethod() && !isInterfaceMethod();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool Function::isClassMethod() const
{
	return m_handle->IsClassMethod();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool Function::isInterfaceMethod() const
{
	return m_handle->IsInterfaceMethod();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool Function::isReadOnly() const
{
	return m_handle->IsReadOnly();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Type::opt Function::objectType() const
{
	auto type = pointer<asIObjectType>(m_handle->GetObjectType());
	if(type == nullptr)
		return Type::opt();
	else
		return reflectable()->type(type->GetTypeId());
}
///////////////////////////////////////////////////////////////////////////////////////////////////




Type Function::returnType() const
{
	int id = m_handle->GetReturnTypeId();
	if(id < 0)
		return reflectable()->type(asTYPEID_VOID);
	else
		return reflectable()->type(id);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

int Function::argumentCount() const
{
	return m_handle->GetParamCount();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Type Function::argument(int index) const
{
	verify(index < argumentCount());

	int id = m_handle->GetParamTypeId(index);
	return reflectable()->type(id);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Reflectable::const_ptr Function::reflectable() const
{
	return Reflectable::const_ptr(m_reflectable);
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void Function::compareState(boostext::iostreams::bin_istream& stream) const
{
	int tmp;
	stream >> tmp;
	if(tmp != id())
		throw invalid_value_exception("function id doesn't match", tmp);

	QByteArray data;
	stream >> data;
	QString n = QString::fromUtf8(data.constData(), data.size());
	if(n != name())
		throw invalid_value_exception("function name doesn't match", tmp);

	stream >> tmp;
	if(tmp != returnType().id())
		throw invalid_value_exception("function return type id doesn't match", tmp);

	stream >> tmp;
	if(tmp != argumentCount())
		throw invalid_value_exception("function argument count doesn't match", tmp);

	for(int i = 0; i < argumentCount(); ++i)
	{
		stream >> tmp;
		if(tmp != argument(i).id())
			throw invalid_value_exception("function argument type id doesn't match", tmp);
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Function::saveState(boostext::iostreams::bin_ostream& stream) const
{
	stream << id();
	stream << declaration().toUtf8();
	stream << returnType().id();
	stream << argumentCount();
	for(int i = 0; i < argumentCount(); ++i)
		stream << argument(i).id();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
